{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# default_exp models.ROCKET"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# ROCKET\n",
    "\n",
    "> ROCKET (RandOm Convolutional KErnel Transform) functions for univariate and multivariate time series."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "from tsai.imports import *\n",
    "from tsai.data.external import *\n",
    "from tsai.models.layers import *"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "from sklearn.linear_model import RidgeClassifierCV, RidgeCV\n",
    "from sktime.transformations.panel.rocket import Rocket\n",
    "from numba import njit, prange"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "# This is an unofficial ROCKET implementation in Pytorch developed by Ignacio Oguiza - oguiza@gmail.com based on:\n",
    "# Angus Dempster, Francois Petitjean, Geoff Webb\n",
    "# Dempster A, Petitjean F, Webb GI (2019) ROCKET: Exceptionally fast and\n",
    "# accurate time series classification using random convolutional kernels.\n",
    "# arXiv:1910.13051\n",
    "# Official repo: https://github.com/angus924/rocket\n",
    "\n",
    "# changes: \n",
    "# - added kss parameter to generate_kernels\n",
    "# - convert X to np.float64\n",
    "\n",
    "def generate_kernels(input_length, num_kernels, kss=[7, 9, 11], pad=True, dilate=True):\n",
    "    candidate_lengths = np.array((kss))\n",
    "    # initialise kernel parameters\n",
    "    weights = np.zeros((num_kernels, candidate_lengths.max())) # see note\n",
    "    lengths = np.zeros(num_kernels, dtype = np.int32) # see note\n",
    "    biases = np.zeros(num_kernels)\n",
    "    dilations = np.zeros(num_kernels, dtype = np.int32)\n",
    "    paddings = np.zeros(num_kernels, dtype = np.int32)\n",
    "    # note: only the first *lengths[i]* values of *weights[i]* are used\n",
    "    for i in range(num_kernels):\n",
    "        length = np.random.choice(candidate_lengths)\n",
    "        _weights = np.random.normal(0, 1, length)\n",
    "        bias = np.random.uniform(-1, 1)\n",
    "        if dilate: dilation = 2 ** np.random.uniform(0, np.log2((input_length - 1) // (length - 1)))\n",
    "        else: dilation = 1\n",
    "        if pad: padding = ((length - 1) * dilation) // 2 if np.random.randint(2) == 1 else 0\n",
    "        else: padding = 0\n",
    "        weights[i, :length] = _weights - _weights.mean()\n",
    "        lengths[i], biases[i], dilations[i], paddings[i] = length, bias, dilation, padding\n",
    "    return weights, lengths, biases, dilations, paddings\n",
    "\n",
    "@njit(fastmath = True)\n",
    "def apply_kernel(X, weights, length, bias, dilation, padding):\n",
    "    # zero padding\n",
    "    if padding > 0:\n",
    "        _input_length = len(X)\n",
    "        _X = np.zeros(_input_length + (2 * padding))\n",
    "        _X[padding:(padding + _input_length)] = X\n",
    "        X = _X\n",
    "    input_length = len(X)\n",
    "    output_length = input_length - ((length - 1) * dilation)\n",
    "    _ppv = 0 # \"proportion of positive values\"\n",
    "    _max = np.NINF\n",
    "    for i in range(output_length):\n",
    "        _sum = bias\n",
    "        for j in range(length):\n",
    "            _sum += weights[j] * X[i + (j * dilation)]\n",
    "        if _sum > 0:\n",
    "            _ppv += 1\n",
    "        if _sum > _max:\n",
    "            _max = _sum\n",
    "    return _ppv / output_length, _max\n",
    "\n",
    "@njit(parallel = True, fastmath = True)\n",
    "def apply_kernels(X, kernels):\n",
    "    X = X.astype(np.float64)\n",
    "    weights, lengths, biases, dilations, paddings = kernels\n",
    "    num_examples = len(X)\n",
    "    num_kernels = len(weights)\n",
    "    # initialise output\n",
    "    _X = np.zeros((num_examples, num_kernels * 2)) # 2 features per kernel\n",
    "    for i in prange(num_examples):\n",
    "        for j in range(num_kernels):\n",
    "            _X[i, (j * 2):((j * 2) + 2)] = \\\n",
    "            apply_kernel(X[i], weights[j][:lengths[j]], lengths[j], biases[j], dilations[j], paddings[j])\n",
    "    return _X"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#hide\n",
    "X_train, y_train, X_valid, y_valid = get_UCR_data('OliveOil')\n",
    "seq_len = X_train.shape[-1]\n",
    "X_train = X_train[:, 0].astype(np.float64)\n",
    "X_valid = X_valid[:, 0].astype(np.float64)\n",
    "labels = np.unique(y_train)\n",
    "transform = {}\n",
    "for i, l in enumerate(labels): transform[l] = i\n",
    "y_train = np.vectorize(transform.get)(y_train).astype(np.int32)\n",
    "y_valid = np.vectorize(transform.get)(y_valid).astype(np.int32)\n",
    "X_train = (X_train - X_train.mean(axis = 1, keepdims = True)) / (X_train.std(axis = 1, keepdims = True) + 1e-8)\n",
    "X_valid = (X_valid - X_valid.mean(axis = 1, keepdims = True)) / (X_valid.std(axis = 1, keepdims = True) + 1e-8)\n",
    "\n",
    "# only univariate time series of shape (samples, len)\n",
    "kernels = generate_kernels(seq_len, 10000)\n",
    "X_train_tfm = apply_kernels(X_train, kernels)\n",
    "X_valid_tfm = apply_kernels(X_valid, kernels)\n",
    "classifier = RidgeClassifierCV(alphas=np.logspace(-3, 3, 7), normalize=True)\n",
    "classifier.fit(X_train_tfm, y_train)\n",
    "score = classifier.score(X_valid_tfm, y_valid)\n",
    "test_eq(ge(score,.9), True)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "class ROCKET(nn.Module):\n",
    "    \"\"\"RandOm Convolutional KErnel Transform\n",
    "    \n",
    "    ROCKET is a GPU Pytorch implementation of the ROCKET functions generate_kernels\n",
    "    and apply_kernels that can be used  with univariate and multivariate time series.\n",
    "    \"\"\"\n",
    "    \n",
    "    def __init__(self, c_in, seq_len, n_kernels=10_000, kss=[7, 9, 11], device=None, verbose=False):\n",
    "\n",
    "        '''\n",
    "        Input: is a 3d torch tensor of type torch.float32. When used with univariate TS,\n",
    "        make sure you transform the 2d to 3d by adding unsqueeze(1).\n",
    "        c_in: number of channels or features. For univariate c_in is 1.\n",
    "        seq_len: sequence length\n",
    "        '''\n",
    "        super().__init__()\n",
    "        device = ifnone(device, default_device())\n",
    "        kss = [ks for ks in kss if ks < seq_len]\n",
    "        convs = nn.ModuleList()\n",
    "        for i in range(n_kernels):\n",
    "            ks = np.random.choice(kss)\n",
    "            dilation = 2**np.random.uniform(0, np.log2((seq_len - 1) // (ks - 1)))\n",
    "            padding = int((ks - 1) * dilation // 2) if np.random.randint(2) == 1 else 0\n",
    "            weight = torch.randn(1, c_in, ks)\n",
    "            weight -= weight.mean()\n",
    "            bias = 2 * (torch.rand(1) - .5)\n",
    "            layer = nn.Conv1d(c_in, 1, ks, padding=2 * padding, dilation=int(dilation), bias=True)\n",
    "            layer.weight = torch.nn.Parameter(weight, requires_grad=False)\n",
    "            layer.bias = torch.nn.Parameter(bias, requires_grad=False)\n",
    "            convs.append(layer)\n",
    "        self.convs = convs\n",
    "        self.n_kernels = n_kernels\n",
    "        self.kss = kss\n",
    "        self.to(device=device)\n",
    "        self.verbose=verbose\n",
    "\n",
    "    def forward(self, x):\n",
    "        _output = []\n",
    "        for i in progress_bar(range(self.n_kernels), display=self.verbose, leave=False, comment='kernel/kernels'):\n",
    "            out = self.convs[i](x).cpu()\n",
    "            _max = out.max(dim=-1)[0]\n",
    "            _ppv = torch.gt(out, 0).sum(dim=-1).float() / out.shape[-1]\n",
    "            _output.append(_max)\n",
    "            _output.append(_ppv)\n",
    "        return torch.cat(_output, dim=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "def create_rocket_features(dl, model, verbose=False):\n",
    "    \"\"\"Args:\n",
    "        model     : ROCKET model instance\n",
    "        dl        : single TSDataLoader (for example dls.train or dls.valid)\n",
    "    \"\"\"\n",
    "    _x_out = []\n",
    "    _y_out = []\n",
    "    for i,(xb,yb) in enumerate(progress_bar(dl, display=verbose, leave=False, comment='batch/batches')):\n",
    "        _x_out.append(model(xb).cpu())\n",
    "        _y_out.append(yb.cpu())\n",
    "    return torch.cat(_x_out).numpy(), torch.cat(_y_out).numpy()\n",
    "\n",
    "get_rocket_features = create_rocket_features"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "bs = 16\n",
    "c_in = 7  # aka channels, features, variables, dimensions\n",
    "c_out = 2\n",
    "seq_len = 15\n",
    "xb = torch.randn(bs, c_in, seq_len).to(default_device())\n",
    "\n",
    "m = ROCKET(c_in, seq_len, n_kernels=1_000, kss=[7, 9, 11]) # 1_000 for testing with a cpu. Default is 10k with a gpu!\n",
    "test_eq(m(xb).shape, [bs, 2_000])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "((30, 2000), (30, 2000))"
      ]
     },
     "execution_count": null,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from tsai.data.all import *\n",
    "from tsai.models.utils import *\n",
    "X, y, splits = get_UCR_data('OliveOil', split_data=False)\n",
    "tfms = [None, TSRegression()]\n",
    "batch_tfms = TSStandardize(by_var=True)\n",
    "dls = get_ts_dls(X, y, splits=splits, tfms=tfms, batch_tfms=batch_tfms, shuffle_train=False, drop_last=False)\n",
    "model = build_ts_model(ROCKET, dls=dls, n_kernels=1_000) # 1_000 for testing with a cpu. Default is 10k with a gpu!\n",
    "X_train, y_train = create_rocket_features(dls.train, model) \n",
    "X_valid, y_valid = create_rocket_features(dls.valid, model)\n",
    "X_train.shape, X_valid.shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "class RocketClassifier(sklearn.pipeline.Pipeline):\n",
    "    \"\"\"Time series classification using ROCKET features and a linear classifier\"\"\"\n",
    "    \n",
    "    def __init__(self, num_kernels=10_000, normalize_input=True, random_state=None, \n",
    "                 alphas=np.logspace(-3, 3, 7), normalize_features=True, memory=None, verbose=False, scoring=None, class_weight=None, **kwargs):\n",
    "        \"\"\"\n",
    "        RocketClassifier is recommended for up to 10k time series. \n",
    "        For a larger dataset, you can use ROCKET (in Pytorch).\n",
    "        scoring = None --> defaults to accuracy.\n",
    "        \n",
    "        Rocket args:            \n",
    "            num_kernels     : int, number of random convolutional kernels (default 10,000)\n",
    "            normalize_input : boolean, whether or not to normalise the input time series per instance (default True)\n",
    "            random_state    : int (ignored unless int due to compatability with Numba), random seed (optional, default None)\n",
    "\n",
    "        \"\"\"\n",
    "        self.steps = [('rocket', Rocket(num_kernels=num_kernels, normalise=normalize_input, random_state=random_state)),\n",
    "                      ('ridgeclassifiercv', RidgeClassifierCV(alphas=alphas, normalize=normalize_features, scoring=scoring, \n",
    "                                                              class_weight=class_weight, **kwargs))]\n",
    "        store_attr()\n",
    "        self._validate_steps()\n",
    "\n",
    "    def __repr__(self):  \n",
    "        return f'Pipeline(steps={self.steps.copy()})'\n",
    "\n",
    "    def save(self, fname='Rocket', path='./models'):\n",
    "        path = Path(path)\n",
    "        filename = path/fname\n",
    "        with open(f'{filename}.pkl', 'wb') as output:\n",
    "            pickle.dump(self, output, pickle.HIGHEST_PROTOCOL)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "def load_rocket(fname='Rocket', path='./models'):\n",
    "    path = Path(path)\n",
    "    filename = path/fname\n",
    "    with open(f'{filename}.pkl', 'rb') as input:\n",
    "        output = pickle.load(input)\n",
    "    return output"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "#export\n",
    "class RocketRegressor(sklearn.pipeline.Pipeline):\n",
    "    \"\"\"Time series regression using ROCKET features and a linear regressor\"\"\"\n",
    "    \n",
    "    def __init__(self, num_kernels=10_000, normalize_input=True, random_state=None, \n",
    "                 alphas=np.logspace(-3, 3, 7), normalize_features=True, memory=None, verbose=False, scoring=None, **kwargs):\n",
    "        \"\"\"\n",
    "        RocketRegressor is recommended for up to 10k time series. \n",
    "        For a larger dataset, you can use ROCKET (in Pytorch).\n",
    "        scoring = None --> defaults to r2.\n",
    "        \n",
    "        Args:            \n",
    "            num_kernels     : int, number of random convolutional kernels (default 10,000)\n",
    "            normalize_input : boolean, whether or not to normalise the input time series per instance (default True)\n",
    "            random_state    : int (ignored unless int due to compatability with Numba), random seed (optional, default None)\n",
    "        \"\"\"\n",
    "        self.steps = [('rocket', Rocket(num_kernels=num_kernels, normalise=normalize_input, random_state=random_state)),\n",
    "                      ('ridgecv', RidgeCV(alphas=alphas, normalize=normalize_features, scoring=scoring, **kwargs))]\n",
    "        store_attr()\n",
    "        self._validate_steps()\n",
    "\n",
    "    def __repr__(self):  \n",
    "        return f'Pipeline(steps={self.steps.copy()})'\n",
    "\n",
    "    def save(self, fname='Rocket', path='./models'):\n",
    "        path = Path(path)\n",
    "        filename = path/fname\n",
    "        with open(f'{filename}.pkl', 'wb') as output:\n",
    "            pickle.dump(self, output, pickle.HIGHEST_PROTOCOL)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.9\n"
     ]
    }
   ],
   "source": [
    "# Univariate classification with sklearn-type API\n",
    "dsid = 'OliveOil'\n",
    "fname = 'RocketClassifier'\n",
    "X_train, y_train, X_test, y_test = get_UCR_data(dsid, Xdtype='float64')\n",
    "cls = RocketClassifier()\n",
    "cls.fit(X_train, y_train)\n",
    "cls.save(fname)\n",
    "del cls\n",
    "cls = load_rocket(fname)\n",
    "print(cls.score(X_test, y_test))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.8888888888888888\n"
     ]
    }
   ],
   "source": [
    "# Multivariate classification with sklearn-type API\n",
    "dsid = 'NATOPS'\n",
    "fname = 'RocketClassifier'\n",
    "X_train, y_train, X_test, y_test = get_UCR_data(dsid, Xdtype='float64')\n",
    "cls = RocketClassifier()\n",
    "cls.fit(X_train, y_train)\n",
    "cls.save(fname)\n",
    "del cls\n",
    "cls = load_rocket(fname)\n",
    "print(cls.score(X_test, y_test))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.042516063160167095\n"
     ]
    }
   ],
   "source": [
    "# Univariate regression with sklearn-type API\n",
    "from sklearn.metrics import mean_squared_error\n",
    "dsid = 'Covid3Month'\n",
    "fname = 'RocketRegressor'\n",
    "X_train, y_train, X_test, y_test = get_Monash_regression_data(dsid, Xdtype='float64')\n",
    "if X_train is not None: \n",
    "    rmse_scorer = make_scorer(mean_squared_error, greater_is_better=False)\n",
    "    reg = RocketRegressor(scoring=rmse_scorer)\n",
    "    reg.fit(X_train, y_train)\n",
    "    reg.save(fname)\n",
    "    del reg\n",
    "    reg = load_rocket(fname)\n",
    "    y_pred = reg.predict(X_test)\n",
    "    print(mean_squared_error(y_test, y_pred, squared=False))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2.361209794063753\n"
     ]
    }
   ],
   "source": [
    "# Multivariate regression with sklearn-type API\n",
    "from sklearn.metrics import mean_squared_error\n",
    "dsid = 'AppliancesEnergy'\n",
    "fname = 'RocketRegressor'\n",
    "X_train, y_train, X_test, y_test = get_Monash_regression_data(dsid, Xdtype='float64')\n",
    "if X_train is not None: \n",
    "    rmse_scorer = make_scorer(mean_squared_error, greater_is_better=False)\n",
    "    reg = RocketRegressor(scoring=rmse_scorer)\n",
    "    reg.fit(X_train, y_train)\n",
    "    reg.save(fname)\n",
    "    del reg\n",
    "    reg = load_rocket(fname)\n",
    "    y_pred = reg.predict(X_test)\n",
    "    print(mean_squared_error(y_test, y_pred, squared=False))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "IPython.notebook.save_checkpoint();"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "111_models.ROCKET.ipynb saved at 2021-11-23 14:42:29.\n",
      "Converted 111_models.ROCKET.ipynb.\n",
      "\n",
      "\n",
      "Correct conversion! 😃\n",
      "Total time elapsed 0.009 s\n",
      "Tuesday 23/11/21 14:42:32 CET\n"
     ]
    },
    {
     "data": {
      "text/html": [
       "\n",
       "                <audio  controls=\"controls\" autoplay=\"autoplay\">\n",
       "                    <source src=\"data:audio/wav;base64,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\" type=\"audio/wav\" />\n",
       "                    Your browser does not support the audio element.\n",
       "                </audio>\n",
       "              "
      ],
      "text/plain": [
       "<IPython.lib.display.Audio object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#hide\n",
    "from tsai.imports import create_scripts\n",
    "from tsai.export import get_nb_name\n",
    "nb_name = get_nb_name()\n",
    "create_scripts(nb_name);"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
